【MCU】把"安卓HAL层思想"引到单片机软件开发中
1、聊一聊
在之前的文章中bug菌有提到过stm32的HAL层,而该层与安卓的硬件抽象层却迥然不同,相对而言stm32的HAL层更像是每个外设的驱动程序,所以这里bug菌把安卓HAL层的思想引到MCU中来跟大家聊聊!
2、话题引出
1
什么是HAL层 ?
1
Hal简介
HAL层又叫硬件抽象层,英文全名为(Hardware Abstraction Layer),从名字上就觉得高大上,就像字面意思这个层面会把所有的硬件都进行抽象,它就像一款万能的遥控器,可以操作所有的旧彩电、冰箱、洗衣机、空调、热水器......(怎么感觉像收废品的)。
(上图安卓框架源于网络)
上图是经典的安卓架构图,然而HAL层的提出最早并不是在Linux中出现,而是由Microsoft公司为确保WindowsNT的稳定性和兼容性而提出的,Microsoft发现直接操作硬件不仅会带来软件上的不兼容,而且对系统稳定性也是不利因素,所以就有了类似于虚拟机这样的HAL层。
2
win VS 安卓
Windows下的HAL层是位于物理硬件设备之上的,也就是说该HAL层直接操作底层寄存器,从而形成一套统一的HAL层接口给上层操作系统使用的,那么所编写的硬件驱动都需要满足HAL层的规范才能够被使用。
而对于安卓系统所实现的HAL却与win有所差异,大家可以看看前面安卓系统层次图,其HAL层位于操作系统内核和驱动程序之上,且位于用户空间。
那么为什么会形成如此的差异呢?
由于window不开源而Linux内核需要遵循的是GPL开源协议,说白了就是要开源代码,但是基于Linux开发的硬件驱动和应用程序不必遵循GPL许可。
为了保障各个厂家的利益,所以就有了HAL层的诞生,当然HAL层虽然位于内核之上,不过其功能还是通过系统调用与底层打交道,所以也叫用户空间驱动程序。那么不想开源的厂家就可以把关键的处理逻辑放到HAL层并且加密处理以后发布,而不需要开放,而内核部分只需要基础的与底层交互的机制即可。
2
安卓HAL层实现思路
这里不会详细的讲解每一步是如何实现的,仅仅把系统的结构理顺一下,并且把关键的调用关系理一理,让系统HAL层设计的思想为我们所用,所以这里我们只分析三层与C程序关联更多的部分,HAL层以上均认为是应用层,如下图所示 :
1
应用层-->HAL层
我们都知道安卓上都是用java语言开发,而到了HAL以及底层都是用C或者C++,那么从上层服务层到HAL层就必须要有一套JAVA转C/C++的接口实现,比如JNI,只要遵循该规范 Java 代码就能够与其它编程语言互相操作,也允许 Java代码调用 C/C++或汇编语言编写的程序和库。
那么HAL层就只需要为上层提供必须要的方法供其调用即可。
2
HAL层-->底层寄存器
这个层面应该是对抽象硬件非常值得参考的一部分,这里我们通过一些代码研究一下:
HAL层三大结构体 :
hw_module_t
hw_module_methods_t
hw_device_t
从上面的三大结构体的结构体和包含关系我们大致可以了解到,HAL层的都是以硬件模块类module进行抽象的,每个module里面会有一个methods方法,而methods结构体中仅仅包含Open指针,通过向该Open中传入id即可获得一个device指针对象,同时每个device都会存储硬件设备的公共属性和操作方法,并指向相应的module类型。
感觉有点抽象,其实都是固定的一些套路,我觉得来点个灯就会大彻大悟了!
1、首先采用面向对象的思路分别继承两个重要的结构体,这样就可以通过父类common(也叫通用结构)在外部传递从而保持接口的统一,而内部通过强制类型转化为之类led_xxx_t来获得更多的操作接口丰富内部实现。
2、这里的Open函数相当于一个提取器,把子类设备实例化dev以后通过父类device对象指针传递出去,从而给JNI层,这样JNI层就可以直接调用Set_LED方法了。
3、HAL_MODULE_INFO_SYM是和上层应用约定的结构,Android 会根据这些元数据来找到并正确加载 HAL 模块。JNI层通过调用hw_get_module函数(该函数内部会通过id和load函数加载模块,即前面实现的那些内容),hw_get_module函数返回的数据类型为led_module_t型指针, 然后可以通过module->methods->open(module,LED_HARDWARE_MODULE_ID,(struct hw_device_t**)device);来调用hal层的open函数得到led硬件操作的led_control_device_t结构体实例,这样应用程序就可以通过该对象使用对应的接口了。
然而,从上面看来HAL层仅仅只是对硬件进行了抽象,并且其操作的还是系统调用,然而要操作具体的寄存器还需要通过系统调用进入到Linux内核以Open/Write/Read/Ioctl等等接口来进行操作,这一块又是操作平时的cdev和file_Operation结构套路了。
所以在HAL可以实现大部分的逻辑和命令处理,而对应的驱动程序相对比较单一,简单的配置和发送数据等即可,所以安卓的HAL层的最终目的中不仅仅满足了厂家驱动不想开源的想法,而且还达到了抽象底层硬件的目的。
3
HAL层在单片机上的应用
虽然在单片机中没有所谓的用户态和内核态,大部分情况也不需要动态加载,但是相关的抽象思想是完全可以模仿的,比如用C实现面向对象的继承方式;把系统抽象为模块和设备,比如一类音频模块包括USB音频设备,蓝牙音频设备等等;平台相关驱动和逻辑分离等等。
如上的module层可以认为是HAL层,我们可以在MCU中引入HAL的三大结构体,从而实现设备接口上的统一和抽象,当我们驱动层发生了变化,只需要重新编写出满足HAL层接口即可。
3、结束语
文本到这里就结束了,单片机仅仅只是一个工具,思维的高度才会能决定代码的质量!
好了,这里是公众号:“最后一个bug”,一个为大家打造的技术知识提升基地,如果你喜欢交流可以添加下方bug菌微信,我拉你加入公众号技术交流群。
推荐好文 点击蓝色字体即可跳转
☞ 【MCU】寄存器、标准库、HAL库、LL库,这么多库!你叫我怎么选?